package org.octopus.data.desensitized.core.builder;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.util.Arrays;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.octopus.data.desensitized.core.annotation.Desensitized;
import org.octopus.data.desensitized.core.annotation.DesensitizedRule;
import org.octopus.data.desensitized.core.util.DesensitizedUtils;

/**
 * 数据脱敏：重写toString方法实现数据脱敏
 */
public class DataDesensitizedToStringBuilder extends ReflectionToStringBuilder {

    private boolean excludeNullValues = true;

    /**
     * @param object
     */
    public DataDesensitizedToStringBuilder(Object object) {
        super(object);
    }

    /**
     * @param object
     * @param style
     */
    public DataDesensitizedToStringBuilder(Object object, ToStringStyle style) {
        super(object, style);
    }

    /**
     * @param object
     * @param excludeNullValues
     * @param style
     */
    public DataDesensitizedToStringBuilder(Object object, boolean excludeNullValues, ToStringStyle style) {
        super(object, style);
        this.excludeNullValues = excludeNullValues;
    }

    /**
     * @param clazz
     * @return void
     * @description
     * @author lemon
     * @date 2020-07-03 17:29
     */
    @Override
    protected void appendFieldsIn(Class clazz) {
        if (clazz.isArray()) {
            this.reflectionAppendArray(this.getObject());
            return;
        }

        Field[] fields = clazz.getDeclaredFields();
        AccessibleObject.setAccessible(fields, true);
        Arrays.stream(fields).forEach(this::appendFieldIn);
    }

    /**
     * @param field
     */
    protected void appendFieldIn(Field field) {
        if (field.getType().isPrimitive() && !field.getType().equals(String.class)) {
            return;
        }

        String fieldName = field.getName();

        if (!this.accept(field)) {
            return;
        }

        try {
            Object fieldValue = this.getValue(field);

            if (this.excludeNullValues && fieldValue == null) {
                return;
            }

            // 获取字段的脱敏规则
            DesensitizedRule desensitizedRule = this.obtainDesensitized(field);

            if (desensitizedRule == null || !field.getType().equals(String.class)) {
                this.append(fieldName, fieldValue);
            } else {
                this.appendMaskFieldIn(fieldName, (String) fieldValue, desensitizedRule);
            }
        } catch (IllegalAccessException ex) {
            throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
        }
    }

    /**
     * 获取字段的脱敏规则
     *
     * @param field
     * @return
     */
    protected DesensitizedRule obtainDesensitized(Field field) {
        Desensitized desensitized = field.getAnnotation(Desensitized.class);

        if (desensitized == null) {
            return null;
        }

        return DesensitizedUtils.buildDesensitizedRule(desensitized);
    }

    /**
     * @param fieldName
     * @param fieldValue
     * @param desensitizedRule
     */
    protected void appendMaskFieldIn(String fieldName, String fieldValue, DesensitizedRule desensitizedRule) {
        this.append(fieldName, DesensitizedUtils.replaceMaskSymbol(fieldValue, desensitizedRule));
    }
}
